package de.jmda.sample.junit;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.junit.Test;

import de.jmda.gen.GeneratorException;

/**
 * Provides the {@link #run(File, File)} method that creates a JUnit test suite
 * class in <code>targetDirectory</code> for each package in <code>rootDirectory
 * </code> that contains at least one method with a JUnit @{@link Test}
 * annotation.
 * <p>
 * Additionally another JUnit test suite class is generated that calls all the
 * JUnit test suite classes that had been generated before.
 *
 * @author roger.jmda@gmail.com
 */
public abstract class TestSuiteFileWriter
{
	private final static Logger LOGGER =
			Logger.getLogger(TestSuiteFileWriter.class);

	public static void run(File rootDirectory)
			throws IOException, GeneratorException
	{
		run(rootDirectory, null);
	}

	/**
	 * @param rootDirOfFilesToProcess root directory of files that will be
	 *                                processed (analysed)
	 * @param rootDirForGeneratedFiles root directory of files that will be
	 *                                 generated
	 * @throws IOException
	 * @throws GeneratorException
	 */
	public static void run(
			File rootDirOfFilesToProcess, File rootDirForGeneratedFiles)
			throws IOException, GeneratorException
	{
		// parameter check
		if (rootDirOfFilesToProcess == null)
		{
			throw new IllegalArgumentException(
					"root directory of files to process must not be null");
		}
		if (rootDirOfFilesToProcess.exists() == false)
		{
			throw new IOException(
					"root directory of files to process [" +
					rootDirOfFilesToProcess.getAbsolutePath() + "] does not exist");
		}
		if (rootDirForGeneratedFiles == null)
		{
			// use default
			rootDirForGeneratedFiles = new File("src/gen/java");
		}
		if (rootDirForGeneratedFiles.exists() == false)
		{
			FileUtils.forceMkdir(rootDirForGeneratedFiles);
		}
		// parameter check end

		Map<PackageElement, Set<TypeElement>> testSuiteData =
				TestSuiteDataBuilder.build(rootDirOfFilesToProcess);

		TestSuiteBuilder testSuiteBuilder = new TestSuiteBuilder(testSuiteData);
		
		// create a JUnit test suite for each package containing a method with a
		// JUnit @Test annotation
		Map<PackageElement, StringBuffer> packageSuites =
				testSuiteBuilder.buildPackageTestSuites();

		for (PackageElement packageElement : packageSuites.keySet())
		{
			File file =
					new File(
							rootDirForGeneratedFiles,
							packageElement.toString()
							              .replaceAll("\\.", "/") +
							              		"/" + TestSuiteBuilder.CLASSNAME + ".java");
			FileUtils.writeStringToFile(
					file, packageSuites.get(packageElement).toString());
			LOGGER.debug("\nwrote package test suite " + file.getAbsolutePath());
		}

		// create a JUnit test suite calling all the JUnit test suites from
		// buildPackageTestSuites()
		StringBuffer suiteForPackageTestSuites =
				testSuiteBuilder.buildSuiteForPackageTestSuites(
						packageSuites.keySet());
		File file =
				new File(rootDirForGeneratedFiles, TestSuiteBuilder.CLASSNAME + "s.java");
		FileUtils.writeStringToFile(file, suiteForPackageTestSuites.toString());
		LOGGER.debug(
				"\nwrote test suite for package test suites " + file.getAbsolutePath());
	}
}